Системное программирование

Qt Framework и разработка графических приложений

Системное программирование

Qt Framework: Кроссплатформенная разработка GUI-приложений

Qt - это мощный кроссплатформенный фреймворк для разработки графических приложений на языке C++. Он предоставляет богатый набор инструментов для создания современных пользовательских интерфейсов, работы с сетью, базами данных и многого другого.

Qt Framework и разработка графических приложений
Системное программирование

План лекции:

  1. Что такое Qt? История и особенности
  2. Архитектура Qt
  3. Система сигналов и слотов
  4. Базовые классы Qt
  5. Создание оконных приложений
  6. Макеты и управление размещением
  7. Работа с событиями
  8. Графика и рисование
  9. Работа с файлами и ресурсами
  10. Сетевое программирование в Qt
  11. Многопоточность в Qt
  12. Разработка под Linux
  13. Создание простого приложения
  14. Qt Creator и инструменты разработки
  15. Заключение
Qt Framework и разработка графических приложений
Системное программирование

1. Что такое Qt? История и особенности

Qt - это кроссплатформенный фреймворк для разработки приложений с графическим интерфейсом и не только. Разработан компанией Trolltech в 1995 году.

Основные особенности:

  • Кроссплатформенность - Windows, Linux, macOS, мобильные платформы
  • Объектно-ориентированный дизайн - основан на C++
  • Система сигналов и слотов - уникальный механизм связи между объектами
  • Богатый набор классов - GUI, сети, базы данных, XML, JSON и др.
  • Интернационализация - поддержка множества языков
  • Open Source - доступен под LGPL/GPL и коммерческой лицензией
Qt Framework и разработка графических приложений
Системное программирование

Преимущества Qt:

  • Метаобъектная система - расширение возможностей C++
  • Автоматическое управление памятью - система владения объектами
  • Встроенные стили - современный внешний вид без дополнительных усилий
  • Мощные инструменты разработки - Qt Creator, Qt Designer
  • Активное сообщество - большое количество документации и примеров
Qt Framework и разработка графических приложений
Системное программирование

2. Архитектура Qt

Основные компоненты Qt:

#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QVBoxLayout>

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);  // Управляет приложением
    
    QWidget window;                // Главное окно
    window.setWindowTitle("Hello Qt");
    window.resize(300, 200);
    
    QPushButton button("Click me!", &window);  // Кнопка
    button.move(100, 80);
    
    window.show();                 // Показать окно
    return app.exec();             // Запуск цикла обработки событий
}
Qt Framework и разработка графических приложений
Системное программирование

Иерархия классов Qt:

QObject (базовый класс с метаобъектной системой)
├── QWidget (базовый класс для всех виджетов)
│   ├── QMainWindow (главное окно приложения)
│   ├── QDialog (диалоговые окна)
│   ├── QAbstractButton
│   │   ├── QPushButton
│   │   ├── QCheckBox
│   │   └── QRadioButton
│   ├── QLineEdit (однострочное текстовое поле)
│   ├── QTextEdit (многострочное текстовое поле)
│   └── QLabel (текстовая метка)
├── QThread (потоки выполнения)
└── QTcpSocket (сетевые сокеты)
Qt Framework и разработка графических приложений
Системное программирование

3. Система сигналов и слотов

Система сигналов и слотов - это механизм связи между объектами Qt, который позволяет объектам отправлять друг другу сообщения.

Преимущества:

  • Типобезопасность - проверка типов на этапе компиляции
  • Слабая связанность - объекты не знают друг о друге напрямую
  • Гибкость - один сигнал может быть подключен к нескольким слотам
Qt Framework и разработка графических приложений
Системное программирование

Пример сигналов и слотов:

#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QVBoxLayout>
#include <QLabel>

class MyWidget : public QWidget {
    Q_OBJECT  // Макрос для метаобъектной системы
    
public:
    MyWidget(QWidget *parent = nullptr) : QWidget(parent) {
        // Создание виджетов
        button = new QPushButton("Click me!", this);
        label = new QLabel("Hello, World!", this);
        
        // Компоновка
        QVBoxLayout *layout = new QVBoxLayout(this);
        layout->addWidget(label);
        layout->addWidget(button);
        
        // Соединение сигнала и слота
        connect(button, &QPushButton::clicked, 
                this, &MyWidget::onButtonClicked);
    }
    
private slots:  // Слоты - это специальные функции-обработчики
    void onButtonClicked() {
        label->setText("Button clicked!");
        button->setEnabled(false);
    }
    
private:
    QPushButton *button;
    QLabel *label;
};
Qt Framework и разработка графических приложений
Системное программирование

Различные способы соединения:

// Старый синтаксис (Qt 4)
connect(sender, SIGNAL(signal()), receiver, SLOT(slot()));

// Новый синтаксис (Qt 5+) - рекомендуемый
connect(sender, &Sender::signal, receiver, &Receiver::slot);

// Соединение с лямбда-функцией
connect(button, &QPushButton::clicked, []() {
    qDebug() << "Button clicked!";
});

// Соединение нескольких слотов к одному сигналу
connect(button, &QPushButton::clicked, 
        this, &MyWidget::slot1);
connect(button, &QPushButton::clicked, 
        this, &MyWidget::slot2);
Qt Framework и разработка графических приложений
Системное программирование

4. Базовые классы Qt

QString - строки в Qt:

QString str1 = "Hello";
QString str2 = "World";
QString result = str1 + " " + str2;  // "Hello World"

// Полезные методы
int length = result.length();
QString upper = result.toUpper();
QString lower = result.toLower();
bool contains = result.contains("World");

// Конвертация
std::string stdStr = result.toStdString();
const char *cStr = result.toLocal8Bit().constData();
Qt Framework и разработка графических приложений
Системное программирование

QVector, QList, QMap - контейнеры:

#include <QVector>
#include <QList>
#include <QMap>

QVector<int> numbers = {1, 2, 3, 4, 5};
numbers.append(6);
int first = numbers.first();
int last = numbers.last();

QList<QString> names;
names << "Alice" << "Bob" << "Charlie";
names.removeAt(1);  // Удалить "Bob"

QMap<QString, int> ages;
ages["Alice"] = 25;
ages["Bob"] = 30;
int aliceAge = ages["Alice"];
Qt Framework и разработка графических приложений
Системное программирование

QFile и QTextStream - работа с файлами:

#include <QFile>
#include <QTextStream>
#include <QDebug>

// Чтение файла
QFile file("data.txt");
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
    QTextStream in(&file);
    while (!in.atEnd()) {
        QString line = in.readLine();
        qDebug() << line;
    }
    file.close();
}

// Запись в файл
QFile outFile("output.txt");
if (outFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
    QTextStream out(&outFile);
    out << "Hello, Qt!" << Qt::endl;
    out << "Writing to file" << Qt::endl;
    outFile.close();
}
Qt Framework и разработка графических приложений
Системное программирование

5. Создание оконных приложений

QMainWindow - главное окно приложения:

#include <QMainWindow>
#include <QMenuBar>
#include <QToolBar>
#include <QStatusBar>
#include <QTextEdit>

class MainWindow : public QMainWindow {
    Q_OBJECT
    
public:
    MainWindow(QWidget *parent = nullptr) : QMainWindow(parent) {
        // Настройка окна
        setWindowTitle("Qt Text Editor");
        resize(800, 600);
        
        // Центральный виджет
        textEdit = new QTextEdit(this);
        setCentralWidget(textEdit);
        
        // Создание меню
        createMenus();
        createToolBars();
        createStatusBar();
    }
    
private:
    void createMenus() {
        QMenu *fileMenu = menuBar()->addMenu("&File");
        
        QAction *newAction = fileMenu->addAction("&New");
        newAction->setShortcut(QKeySequence::New);
        connect(newAction, &QAction::triggered, this, &MainWindow::newFile);
        
        QAction *openAction = fileMenu->addAction("&Open");
        openAction->setShortcut(QKeySequence::Open);
        connect(openAction, &QAction::triggered, this, &MainWindow::openFile);
        
        QAction *exitAction = fileMenu->addAction("E&xit");
        connect(exitAction, &QAction::triggered, this, &QWidget::close);
    }
    
    void createToolBars() {
        QToolBar *fileToolBar = addToolBar("File");
        fileToolBar->addAction("New");
        fileToolBar->addAction("Open");
    }
    
    void createStatusBar() {
        statusBar()->showMessage("Ready");
    }
    
private slots:
    void newFile() {
        textEdit->clear();
        setWindowTitle("New Document");
    }
    
    void openFile() {
        QString fileName = QFileDialog::getOpenFileName(this);
        if (!fileName.isEmpty()) {
            QFile file(fileName);
            if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
                QTextStream in(&file);
                textEdit->setText(in.readAll());
                file.close();
                setWindowTitle(fileName);
            }
        }
    }
    
private:
    QTextEdit *textEdit;
};
Qt Framework и разработка графических приложений
Системное программирование

QDialog - диалоговые окна:

#include <QDialog>
#include <QFormLayout>
#include <QLineEdit>
#include <QDialogButtonBox>

class LoginDialog : public QDialog {
    Q_OBJECT
    
public:
    LoginDialog(QWidget *parent = nullptr) : QDialog(parent) {
        setWindowTitle("Login");
        setModal(true);
        resize(300, 150);
        
        // Создание виджетов
        usernameEdit = new QLineEdit(this);
        passwordEdit = new QLineEdit(this);
        passwordEdit->setEchoMode(QLineEdit::Password);
        
        QDialogButtonBox *buttonBox = new QDialogButtonBox(
            QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
        
        // Компоновка
        QFormLayout *layout = new QFormLayout(this);
        layout->addRow("Username:", usernameEdit);
        layout->addRow("Password:", passwordEdit);
        layout->addWidget(buttonBox);
        
        // Соединения
        connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
        connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
    }
    
    QString username() const { return usernameEdit->text(); }
    QString password() const { return passwordEdit->text(); }
    
private:
    QLineEdit *usernameEdit;
    QLineEdit *passwordEdit;
};
Qt Framework и разработка графических приложений
Системное программирование

6. Макеты и управление размещением

Основные типы макетов:

  • QVBoxLayout - вертикальное размещение
  • QHBoxLayout - горизонтальное размещение
  • QGridLayout - табличное размещение
  • QFormLayout - размещение форм (метка + поле)
  • QStackedLayout - переключаемые страницы
Qt Framework и разработка графических приложений
Системное программирование

Пример использования макетов:

#include <QWidget>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QGridLayout>
#include <QPushButton>
#include <QLineEdit>
#include <QLabel>

class LayoutExample : public QWidget {
    Q_OBJECT
    
public:
    LayoutExample(QWidget *parent = nullptr) : QWidget(parent) {
        // Основной вертикальный макет
        QVBoxLayout *mainLayout = new QVBoxLayout(this);
        
        // Горизонтальный макет для кнопок
        QHBoxLayout *buttonLayout = new QHBoxLayout();
        buttonLayout->addWidget(new QPushButton("Button 1"));
        buttonLayout->addWidget(new QPushButton("Button 2"));
        buttonLayout->addWidget(new QPushButton("Button 3"));
        
        // Сеточный макет для полей ввода
        QGridLayout *formLayout = new QGridLayout();
        formLayout->addWidget(new QLabel("Name:"), 0, 0);
        formLayout->addWidget(new QLineEdit(), 0, 1);
        formLayout->addWidget(new QLabel("Email:"), 1, 0);
        formLayout->addWidget(new QLineEdit(), 1, 1);
        formLayout->addWidget(new QLabel("Phone:"), 2, 0);
        formLayout->addWidget(new QLineEdit(), 2, 1);
        
        // Добавление макетов в основной
        mainLayout->addLayout(buttonLayout);
        mainLayout->addLayout(formLayout);
        mainLayout->addStretch();  // Растяжка для заполнения свободного пространства
    }
};
Qt Framework и разработка графических приложений
Системное программирование

QFormLayout для форм:

QFormLayout *formLayout = new QFormLayout();

// Простые поля
formLayout->addRow("First Name:", new QLineEdit());
formLayout->addRow("Last Name:", new QLineEdit());

// Поле с паролем
QLineEdit *passwordEdit = new QLineEdit();
passwordEdit->setEchoMode(QLineEdit::Password);
formLayout->addRow("Password:", passwordEdit);

// Поле с подсказкой
QLineEdit *emailEdit = new QLineEdit();
emailEdit->setPlaceholderText("user@example.com");
formLayout->addRow("Email:", emailEdit);

// Кнопки
QDialogButtonBox *buttonBox = new QDialogButtonBox(
    QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
formLayout->addRow(buttonBox);
Qt Framework и разработка графических приложений
Системное программирование

7. Работа с событиями

Обработка событий клавиатуры:

#include <QWidget>
#include <QKeyEvent>
#include <QDebug>

class KeyPressWidget : public QWidget {
    Q_OBJECT
    
public:
    KeyPressWidget(QWidget *parent = nullptr) : QWidget(parent) {
        setFocusPolicy(Qt::StrongFocus);  // Разрешить получение фокуса клавиатуры
    }
    
protected:
    void keyPressEvent(QKeyEvent *event) override {
        qDebug() << "Key pressed:" << event->key()
                 << "Text:" << event->text();
        
        switch (event->key()) {
        case Qt::Key_Escape:
            close();
            break;
        case Qt::Key_F1:
            showHelp();
            break;
        case Qt::Key_Return:
            processInput();
            break;
        default:
            QWidget::keyPressEvent(event);
        }
    }
    
    void keyReleaseEvent(QKeyEvent *event) override {
        qDebug() << "Key released:" << event->key();
    }
    
private:
    void showHelp() {
        qDebug() << "Help system activated";
    }
    
    void processInput() {
        qDebug() << "Processing input";
    }
};
Qt Framework и разработка графических приложений
Системное программирование

Обработка событий мыши:

#include <QWidget>
#include <QMouseEvent>
#include <QPainter>

class PaintWidget : public QWidget {
    Q_OBJECT
    
public:
    PaintWidget(QWidget *parent = nullptr) : QWidget(parent) {
        setMouseTracking(true);  // Отслеживать движение мыши без нажатия кнопок
    }
    
protected:
    void mousePressEvent(QMouseEvent *event) override {
        if (event->button() == Qt::LeftButton) {
            lastPoint = event->pos();
            drawing = true;
        }
    }
    
    void mouseMoveEvent(QMouseEvent *event) override {
        if (drawing && (event->buttons() & Qt::LeftButton)) {
            currentPoint = event->pos();
            update();  // Запрос на перерисовку
        }
    }
    
    void mouseReleaseEvent(QMouseEvent *event) override {
        if (event->button() == Qt::LeftButton) {
            drawing = false;
        }
    }
    
    void paintEvent(QPaintEvent *event) override {
        QPainter painter(this);
        if (drawing) {
            painter.drawLine(lastPoint, currentPoint);
            lastPoint = currentPoint;
        }
    }
    
private:
    bool drawing = false;
    QPoint lastPoint;
    QPoint currentPoint;
};
Qt Framework и разработка графических приложений
Системное программирование

8. Графика и рисование

Использование QPainter:

#include <QWidget>
#include <QPainter>
#include <QPen>
#include <QBrush>

class GraphicsWidget : public QWidget {
    Q_OBJECT
    
protected:
    void paintEvent(QPaintEvent *event) override {
        QPainter painter(this);
        painter.setRenderHint(QPainter::Antialiasing);  // Сглаживание
        
        // Рисование линий
        QPen pen(Qt::blue, 2);
        painter.setPen(pen);
        painter.drawLine(10, 10, 100, 100);
        
        // Рисование прямоугольников
        painter.setBrush(QBrush(Qt::red, Qt::SolidPattern));
        painter.drawRect(120, 10, 100, 80);
        
        // Рисование эллипсов
        painter.setBrush(QBrush(Qt::green, Qt::DiagCrossPattern));
        painter.drawEllipse(240, 10, 100, 80);
        
        // Рисование текста
        painter.setPen(Qt::black);
        painter.setFont(QFont("Arial", 16));
        painter.drawText(10, 150, "Hello, Qt Graphics!");
        
        // Рисование многоугольников
        QPolygon polygon;
        polygon << QPoint(10, 200) << QPoint(100, 200) 
                << QPoint(100, 250) << QPoint(55, 280) 
                << QPoint(10, 250);
        painter.drawPolygon(polygon);
    }
};
Qt Framework и разработка графических приложений
Системное программирование

Анимация с использованием QTimer:

#include <QWidget>
#include <QPainter>
#include <QTimer>
#include <QTime>

class AnimatedWidget : public QWidget {
    Q_OBJECT
    
public:
    AnimatedWidget(QWidget *parent = nullptr) : QWidget(parent) {
        timer = new QTimer(this);
        connect(timer, &QTimer::timeout, this, QOverload<>::of(&QWidget::update));
        timer->start(50);  // Обновление каждые 50 мс
        
        startTime = QTime::currentTime();
    }
    
protected:
    void paintEvent(QPaintEvent *event) override {
        QPainter painter(this);
        painter.setRenderHint(QPainter::Antialiasing);
        
        // Вычисление времени с начала анимации
        int elapsed = startTime.msecsTo(QTime::currentTime());
        float angle = elapsed * 0.1;  // Угол в радианах
        
        // Рисование вращающегося квадрата
        painter.translate(width() / 2, height() / 2);
        painter.rotate(angle * 180 / M_PI);
        
        painter.setBrush(QBrush(Qt::blue));
        painter.drawRect(-50, -50, 100, 100);
    }
    
private:
    QTimer *timer;
    QTime startTime;
};
Qt Framework и разработка графических приложений
Системное программирование

9. Работа с файлами и ресурсами

QFile и QTextStream:

#include <QFile>
#include <QTextStream>
#include <QDebug>
#include <QFileDialog>

class FileManager : public QObject {
    Q_OBJECT
    
public slots:
    void saveToFile() {
        QString fileName = QFileDialog::getSaveFileName(
            nullptr, "Save File", "", "Text Files (*.txt)");
        
        if (!fileName.isEmpty()) {
            QFile file(fileName);
            if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
                QTextStream out(&file);
                out << "This is a test file." << Qt::endl;
                out << "Created with Qt." << Qt::endl;
                file.close();
                qDebug() << "File saved successfully";
            }
        }
    }
    
    void readFromFile() {
        QString fileName = QFileDialog::getOpenFileName(
            nullptr, "Open File", "", "Text Files (*.txt)");
        
        if (!fileName.isEmpty()) {
            QFile file(fileName);
            if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
                QTextStream in(&file);
                QString content = in.readAll();
                qDebug() << "File content:" << content;
                file.close();
            }
        }
    }
};
Qt Framework и разработка графических приложений
Системное программирование

Ресурсы Qt (.qrc файлы):

Файл resources.qrc:

<RCC>
    <qresource prefix="/images">
        <file>icon.png</file>
        <file>logo.jpg</file>
    </qresource>
    <qresource prefix="/styles">
        <file>style.css</file>
    </qresource>
</RCC>
Qt Framework и разработка графических приложений
Системное программирование

Использование ресурсов в коде:

// В .pro файле: RESOURCES += resources.qrc

QPixmap pixmap(":/images/icon.png");
QIcon icon(pixmap);
button->setIcon(icon);

// Чтение CSS из ресурсов
QFile file(":/styles/style.css");
if (file.open(QIODevice::ReadOnly)) {
    QString style = file.readAll();
    qApp->setStyleSheet(style);
}
Qt Framework и разработка графических приложений
Системное программирование

QSettings - сохранение настроек:

#include <QSettings>

class SettingsManager {
public:
    void saveSettings() {
        QSettings settings("MyCompany", "MyApp");
        settings.setValue("window/size", QSize(800, 600));
        settings.setValue("window/position", QPoint(100, 100));
        settings.setValue("user/name", "John Doe");
        settings.setValue("user/theme", "dark");
    }
    
    void loadSettings() {
        QSettings settings("MyCompany", "MyApp");
        QSize size = settings.value("window/size", QSize(640, 480)).toSize();
        QPoint pos = settings.value("window/position", QPoint(0, 0)).toPoint();
        QString userName = settings.value("user/name", "Guest").toString();
        QString theme = settings.value("user/theme", "light").toString();
        
        // Применение настроек
        window->resize(size);
        window->move(pos);
        applyTheme(theme);
    }
    
private:
    void applyTheme(const QString &theme) {
        if (theme == "dark") {
            qApp->setStyleSheet("QWidget { background-color: #333; color: white; }");
        } else {
            qApp->setStyleSheet("");
        }
    }
};
Qt Framework и разработка графических приложений
Системное программирование

10. Сетевое программирование в Qt

QTcpSocket и QTcpServer:

#include <QTcpServer>
#include <QTcpSocket>
#include <QDebug>

class SimpleServer : public QTcpServer {
    Q_OBJECT
    
public:
    SimpleServer(QObject *parent = nullptr) : QTcpServer(parent) {
        if (listen(QHostAddress::Any, 8080)) {
            qDebug() << "Server started on port 8080";
        } else {
            qDebug() << "Server could not start!";
        }
    }
    
protected:
    void incomingConnection(qintptr socketDescriptor) override {
        QTcpSocket *socket = new QTcpSocket(this);
        socket->setSocketDescriptor(socketDescriptor);
        
        qDebug() << "New connection from:" << socket->peerAddress().toString();
        
        connect(socket, &QTcpSocket::readyRead, [socket]() {
            QByteArray data = socket->readAll();
            qDebug() << "Received:" << data;
            
            // Отправка ответа
            socket->write("Hello from Qt server!\n");
            socket->flush();
        });
        
        connect(socket, &QTcpSocket::disconnected, [socket]() {
            qDebug() << "Client disconnected";
            socket->deleteLater();
        });
    }
};
Qt Framework и разработка графических приложений
Системное программирование

TCP-клиент на Qt:

#include <QTcpSocket>
#include <QHostAddress>

class SimpleClient : public QObject {
    Q_OBJECT
    
public:
    SimpleClient(QObject *parent = nullptr) : QObject(parent) {
        socket = new QTcpSocket(this);
        
        connect(socket, &QTcpSocket::connected, this, &SimpleClient::onConnected);
        connect(socket, &QTcpSocket::readyRead, this, &SimpleClient::onReadyRead);
        connect(socket, &QTcpSocket::disconnected, this, &SimpleClient::onDisconnected);
        
        // Подключение к серверу
        socket->connectToHost(QHostAddress::LocalHost, 8080);
    }
    
private slots:
    void onConnected() {
        qDebug() << "Connected to server";
        socket->write("Hello from Qt client!\n");
        socket->flush();
    }
    
    void onReadyRead() {
        QByteArray data = socket->readAll();
        qDebug() << "Server response:" << data;
    }
    
    void onDisconnected() {
        qDebug() << "Disconnected from server";
    }
    
private:
    QTcpSocket *socket;
};
Qt Framework и разработка графических приложений
Системное программирование

QUdpSocket для UDP:

#include <QUdpSocket>

class UdpChat : public QObject {
    Q_OBJECT
    
public:
    UdpChat(quint16 port, QObject *parent = nullptr) : QObject(parent) {
        socket = new QUdpSocket(this);
        
        if (socket->bind(QHostAddress::Any, port)) {
            qDebug() << "UDP socket bound to port" << port;
        }
        
        connect(socket, &QUdpSocket::readyRead, this, &UdpChat::processPendingDatagrams);
    }
    
    void sendMessage(const QString &message, const QHostAddress &address, quint16 port) {
        QByteArray data = message.toUtf8();
        socket->writeDatagram(data, address, port);
    }
    
private slots:
    void processPendingDatagrams() {
        while (socket->hasPendingDatagrams()) {
            QByteArray datagram;
            datagram.resize(socket->pendingDatagramSize());
            
            QHostAddress sender;
            quint16 senderPort;
            
            socket->readDatagram(datagram.data(), datagram.size(),
                               &sender, &senderPort);
            
            QString message = QString::fromUtf8(datagram);
            qDebug() << "Received from" << sender.toString() 
                     << ":" << senderPort << "-" << message;
        }
    }
    
private:
    QUdpSocket *socket;
};
Qt Framework и разработка графических приложений
Системное программирование

11. Многопоточность в Qt

QThread - базовый класс для потоков:

#include <QThread>
#include <QDebug>

class Worker : public QObject {
    Q_OBJECT
    
public slots:
    void doWork() {
        qDebug() << "Work started in thread:" << QThread::currentThread();
        
        for (int i = 0; i < 100; ++i) {
            // Имитация работы
            QThread::msleep(50);
            emit progressChanged(i);
        }
        
        emit workFinished();
    }
    
signals:
    void progressChanged(int value);
    void workFinished();
};

// Использование
QThread *thread = new QThread;
Worker *worker = new Worker;

worker->moveToThread(thread);

connect(thread, &QThread::started, worker, &Worker::doWork);
connect(worker, &Worker::workFinished, thread, &QThread::quit);
connect(worker, &Worker::workFinished, worker, &QObject::deleteLater);
connect(thread, &QThread::finished, thread, &QObject::deleteLater);

thread->start();
Qt Framework и разработка графических приложений
Системное программирование

QtConcurrent для параллельных вычислений:

#include <QtConcurrent>
#include <QFuture>
#include <QFutureWatcher>
#include <algorithm>
#include <vector>

class ConcurrentExample : public QObject {
    Q_OBJECT
    
public:
    ConcurrentExample() {
        // Параллельный map
        std::vector<int> data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        
        QFuture<void> future = QtConcurrent::map(data, [](int &value) {
            value = value * value;  // Возведение в квадрат
        });
        
        QFutureWatcher<void> *watcher = new QFutureWatcher<void>(this);
        connect(watcher, &QFutureWatcher<void>::finished, [data]() {
            qDebug() << "Map completed";
            for (int val : data) {
                qDebug() << val;
            }
        });
        
        watcher->setFuture(future);
    }
};
Qt Framework и разработка графических приложений
Системное программирование

Синхронизация потоков:

#include <QMutex>
#include <QMutexLocker>
#include <QWaitCondition>
#include <QSemaphore>

class ThreadSafeCounter : public QObject {
    Q_OBJECT
    
public:
    void increment() {
        QMutexLocker locker(&mutex);  // Автоматическая блокировка/разблокировка
        count++;
        qDebug() << "Count:" << count;
    }
    
    int value() const {
        QMutexLocker locker(&mutex);
        return count;
    }
    
private:
    mutable QMutex mutex;
    int count = 0;
};

// Пример с QWaitCondition
class ProducerConsumer : public QObject {
    Q_OBJECT
    
public:
    void produce(int value) {
        QMutexLocker locker(&mutex);
        buffer.enqueue(value);
        bufferNotEmpty.wakeOne();  // Разбудить потребителя
    }
    
    int consume() {
        QMutexLocker locker(&mutex);
        while (buffer.isEmpty()) {
            bufferNotEmpty.wait(&mutex);  // Ждать появления данных
        }
        return buffer.dequeue();
    }
    
private:
    QMutex mutex;
    QWaitCondition bufferNotEmpty;
    QQueue<int> buffer;
};
Qt Framework и разработка графических приложений
Системное программирование

12. Разработка под Linux

Установка Qt в Linux:

# Ubuntu/Debian
sudo apt-get install qt5-default qtcreator qtbase5-dev

# Fedora
sudo dnf install qt5-qtbase-devel qt-creator

# Arch Linux
sudo pacman -S qt5-base qtcreator

# Или скачать с официального сайта:
# https://www.qt.io/download
Qt Framework и разработка графических приложений
Системное программирование

Файл проекта Qt (.pro):

# Проект Qt
QT += core gui widgets network concurrent
TARGET = MyQtApp
TEMPLATE = app

# Исходные файлы
SOURCES += main.cpp \
           mainwindow.cpp \
           dialog.cpp

# Заголовочные файлы
HEADERS += mainwindow.h \
           dialog.h

# Файлы форм (если используются)
FORMS += mainwindow.ui \
         dialog.ui

# Ресурсы
RESOURCES += resources.qrc

# Дополнительные настройки
CONFIG += c++14
Qt Framework и разработка графических приложений
Системное программирование

Создание RPM/DEB пакетов:

# Создание RPM пакета в Fedora/RHEL
qmake
make
checkinstall --pkgname=myqtapp --pkgversion="1.0" make install

# Создание DEB пакета в Ubuntu/Debian
qmake
make
checkinstall --pkgname=myqtapp --pkgversion="1.0" make install
Qt Framework и разработка графических приложений
Системное программирование

Интеграция с системой Linux:

// Поддержка иконок темы
QIcon::setThemeName("gnome");  // или "oxygen", "breeze"
QIcon icon = QIcon::fromTheme("document-open");
action->setIcon(icon);

// Работа с системным треем
#include <QSystemTrayIcon>

QSystemTrayIcon *trayIcon = new QSystemTrayIcon(this);
trayIcon->setIcon(QIcon(":/icons/app.png"));
trayIcon->setToolTip("My Qt Application");
trayIcon->show();

// Контекстное меню для трея
QMenu *trayMenu = new QMenu(this);
trayMenu->addAction("Show", this, &QWidget::show);
trayMenu->addAction("Hide", this, &QWidget::hide);
trayMenu->addSeparator();
trayMenu->addAction("Quit", qApp, &QApplication::quit);
trayIcon->setContextMenu(trayMenu);
Qt Framework и разработка графических приложений
Системное программирование

13. Создание простого приложения

Простой текстовый редактор:

// main.cpp
#include <QApplication>
#include "texteditor.h"

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    
    TextEditor editor;
    editor.show();
    
    return app.exec();
}
Qt Framework и разработка графических приложений
Системное программирование
// texteditor.h
#ifndef TEXTEDITOR_H
#define TEXTEDITOR_H

#include <QMainWindow>

QT_BEGIN_NAMESPACE
class QTextEdit;
class QAction;
class QMenu;
QT_END_NAMESPACE

class TextEditor : public QMainWindow {
    Q_OBJECT
    
public:
    TextEditor(QWidget *parent = nullptr);
    
private slots:
    void newFile();
    void open();
    void save();
    void saveAs();
    void about();
    
private:
    void createActions();
    void createMenus();
    void createToolBars();
    void createStatusBar();
    bool maybeSave();
    void loadFile(const QString &fileName);
    bool saveFile(const QString &fileName);
    void setCurrentFile(const QString &fileName);
    QString strippedName(const QString &fullFileName);
    
    QTextEdit *textEdit;
    QString currentFile;
    
    QMenu *fileMenu;
    QMenu *editMenu;
    QMenu *helpMenu;
    
    QToolBar *fileToolBar;
    QToolBar *editToolBar;
    
    QAction *newAction;
    QAction *openAction;
    QAction *saveAction;
    QAction *saveAsAction;
    QAction *exitAction;
    QAction *cutAction;
    QAction *copyAction;
    QAction *pasteAction;
    QAction *aboutAction;
    QAction *aboutQtAction;
};

#endif // TEXTEDITOR_H
Qt Framework и разработка графических приложений
Системное программирование
// texteditor.cpp
#include "texteditor.h"
#include <QtWidgets>

TextEditor::TextEditor(QWidget *parent)
    : QMainWindow(parent), textEdit(new QTextEdit) {
    setCentralWidget(textEdit);
    
    createActions();
    createMenus();
    createToolBars();
    createStatusBar();
    
    readSettings();
    
    connect(textEdit->document(), &QTextDocument::contentsChanged,
            this, &TextEditor::documentWasModified);
    
    setCurrentFile(QString());
    setUnifiedTitleAndToolBarOnMac(true);
}

void TextEditor::createActions() {
    newAction = new QAction(QIcon(":/images/new.png"), tr("&New"), this);
    newAction->setShortcut(QKeySequence::New);
    newAction->setStatusTip(tr("Create a new file"));
    connect(newAction, &QAction::triggered, this, &TextEditor::newFile);
    
    openAction = new QAction(QIcon(":/images/open.png"), tr("&Open..."), this);
    openAction->setShortcut(QKeySequence::Open);
    openAction->setStatusTip(tr("Open an existing file"));
    connect(openAction, &QAction::triggered, this, &TextEditor::open);
    
    saveAction = new QAction(QIcon(":/images/save.png"), tr("&Save"), this);
    saveAction->setShortcut(QKeySequence::Save);
    saveAction->setStatusTip(tr("Save the document to disk"));
    connect(saveAction, &QAction::triggered, this, &TextEditor::save);
}
Qt Framework и разработка графических приложений
Системное программирование

Продолжение texteditor.cpp:

void TextEditor::newFile() {
    if (maybeSave()) {
        textEdit->clear();
        setCurrentFile(QString());
    }
}

void TextEditor::open() {
    if (maybeSave()) {
        QString fileName = QFileDialog::getOpenFileName(this);
        if (!fileName.isEmpty())
            loadFile(fileName);
    }
}

bool TextEditor::save() {
    if (currentFile.isEmpty()) {
        return saveAs();
    } else {
        return saveFile(currentFile);
    }
}

bool TextEditor::saveAs() {
    QFileDialog dialog(this);
    dialog.setWindowModality(Qt::WindowModal);
    dialog.setAcceptMode(QFileDialog::AcceptSave);
    if (dialog.exec() != QDialog::Accepted)
        return false;
    return saveFile(dialog.selectedFiles().first());
}

void TextEditor::loadFile(const QString &fileName) {
    QFile file(fileName);
    if (!file.open(QFile::ReadOnly | QFile::Text)) {
        QMessageBox::warning(this, tr("Application"),
                           tr("Cannot read file %1:\n%2.")
                           .arg(fileName)
                           .arg(file.errorString()));
        return;
    }
    
    QTextStream in(&file);
    textEdit->setPlainText(in.readAll());
    setCurrentFile(fileName);
    statusBar()->showMessage(tr("File loaded"), 2000);
}

bool TextEditor::saveFile(const QString &fileName) {
    QFile file(fileName);
    if (!file.open(QFile::WriteOnly | QFile::Text)) {
        QMessageBox::warning(this, tr("Application"),
                           tr("Cannot write file %1:\n%2.")
                           .arg(fileName)
                           .arg(file.errorString()));
        return false;
    }
    
    QTextStream out(&file);
    out << textEdit->toPlainText();
    setCurrentFile(fileName);
    statusBar()->showMessage(tr("File saved"), 2000);
    return true;
}
Qt Framework и разработка графических приложений
Системное программирование

14. Qt Creator и инструменты разработки

Основные возможности Qt Creator:

  • Интегрированная среда разработки (IDE)
  • Визуальный редактор форм (Qt Designer)
  • Отладчик с поддержкой GDB и CDB
  • Система управления проектами
  • Интеграция с системами контроля версий
  • Автодополнение кода и рефакторинг
  • Профилировщик производительности
Qt Framework и разработка графических приложений
Системное программирование

Работа с Qt Designer:

<!-- mainwindow.ui - XML-представление формы -->
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>800</width>
    <height>600</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <layout class="QVBoxLayout" name="verticalLayout">
    <item>
     <widget class="QPushButton" name="pushButton">
      <property name="text">
       <string>Click me!</string>
      </property>
     </widget>
    </item>
   </layout>
  </widget>
 </widget>
</ui>
Qt Framework и разработка графических приложений
Системное программирование

Использование .ui файлов в коде:

// Автоматически сгенерированный класс Ui::MainWindow
#include "ui_mainwindow.h"

class MainWindow : public QMainWindow {
    Q_OBJECT
    
public:
    MainWindow(QWidget *parent = nullptr) : QMainWindow(parent), ui(new Ui::MainWindow) {
        ui->setupUi(this);  // Инициализация интерфейса
        
        // Подключение сигналов
        connect(ui->pushButton, &QPushButton::clicked, this, &MainWindow::onButtonClicked);
    }
    
    ~MainWindow() {
        delete ui;
    }
    
private slots:
    void onButtonClicked() {
        ui->pushButton->setText("Clicked!");
    }
    
private:
    Ui::MainWindow *ui;
};
Qt Framework и разработка графических приложений
Системное программирование

Инструменты отладки:

// Использование qDebug() для отладки
#include <QDebug>

void debugExample() {
    int value = 42;
    QString text = "Hello";
    
    qDebug() << "Value:" << value;
    qDebug() << "Text:" << text;
    qDebug() << "Multiple values:" << value << text;
    
    // Форматированный вывод
    qDebug("Formatted: %d %s", value, qPrintable(text));
    
    // Уровни отладки
    qInfo() << "Information message";
    qWarning() << "Warning message";
    qCritical() << "Critical error";
    
    // Более подробная информация
    qDebug() << "File:" << __FILE__ << "Line:" << __LINE__;
    qDebug() << "Function:" << Q_FUNC_INFO;
}
Qt Framework и разработка графических приложений
Системное программирование

Профилирование и оптимизация:

// Использование QElapsedTimer для измерения времени
#include <QElapsedTimer>

void measurePerformance() {
    QElapsedTimer timer;
    timer.start();
    
    // Код, который нужно измерить
    heavyComputation();
    
    qint64 elapsed = timer.elapsed();
    qDebug() << "Computation took" << elapsed << "milliseconds";
    
    // Или в микросекундах
    qint64 elapsedMicro = timer.nsecsElapsed() / 1000;
    qDebug() << "Computation took" << elapsedMicro << "microseconds";
}

// Использование QLoggingCategory для управления уровнем логирования
Q_LOGGING_CATEGORY(lcMyApp, "myapp")
Q_LOGGING_CATEGORY(lcNetwork, "myapp.network")

// В коде:
qCDebug(lcMyApp) << "Application debug message";
qCDebug(lcNetwork) << "Network debug message";

// Включается через: QT_LOGGING_RULES="myapp.debug=true"
Qt Framework и разработка графических приложений
Системное программирование

15. Заключение

Преимущества Qt для разработки:

  • Кроссплатформенность - пишем один код, запускаем везде
  • Богатая функциональность - готовые решения для большинства задач
  • Современный дизайн - красивые интерфейсы без дополнительных усилий
  • Активное сообщество - помощь и примеры всегда под рукой
  • Коммерческая поддержка - надежность для Enterprise-проектов
Qt Framework и разработка графических приложений
Системное программирование

Рекомендации по изучению:

  1. Начните с простых примеров - кнопки, метки, базовые макеты
  2. Изучите систему сигналов и слотов - это ключ к пониманию Qt
  3. Практикуйтесь с макетами - правильное размещение элементов
  4. Освойте работу с событиями - для создания интерактивных приложений
  5. Изучите сетевое программирование - для современных приложений
  6. Понимайте многопоточность - для производительных приложений
Qt Framework и разработка графических приложений
Системное программирование

Полезные ресурсы:

  • Официальная документация - https://doc.qt.io/
  • Qt Examples - множество готовых примеров
  • Qt Forum - форум сообщества
  • Stack Overflow - ответы на технические вопросы
  • YouTube каналы - видеоуроки и обучающие материалы

Qt - это мощный инструмент для создания современных кроссплатформенных приложений с графическим интерфейсом!

Qt Framework и разработка графических приложений